#pragma once
#include "threshold_secret_sharing.h"

template<typename T>
struct share_item_CRT
{
public:
    T m0, modulus, remainder;
};

template <typename T> std::ofstream& operator<<(std::ofstream& stream, share_item_CRT<T> n)
{
	stream<<'('<<n.m0<<';'<<n.modulus<<';'<<n.remainder<<')';
	return stream;
}

template <typename T> std::ifstream& operator<<(std::ifstream& stream, share_item_CRT<T> n)
{
	char delim;
	stream >> delim >> n.m0 >> delim >> n.modulus >> delim >>n.remainder>>;
	return stream;
}

template <typename T>
class share_CRT: public ss_share<T > 
{
public:
	share_CRT(const T& value): ss_share(value) {}
};

template <typename T>
class secret_CRT: public ss_secret<T> 
{
public:
	secret_CRT(const T& value): ss_secret(value){	}
};


#include "primes.h"
#include "CRT_helper.h"
#include <time.h>
#include <numeric>
#include <functional>

template<typename T>
class dealer_CRT: public ts_dealer<share_item_CRT<T>, T>
{
public:
    dealer_CRT(const size_t threshold, const size_t number) 
        : ts_dealer(threshold, number)
	{
		srand(int(time(NULL)));
	}
	 
     virtual void distribute_secret(
        const secret_CRT<T> *const secret,
        const ss_players<share_item_CRT<T> >& players) const
     {
		 ts_dealer::distribute_secret(secret, players);
		 const size_t players_count = players.size();
		 const T secret_value = secret->get_secret();

		 std::vector<T> primes = get_primes_for_sharing_CRT<T>(this->_threshold, 
															players_count, 
															secret_value, 
															secret_value * 2);

		
		 
		//T r_product(1);
		//for(numbers i = 1; i <= this->_threshold; ++i) {r_product*=primes[i];}
		T r_product =  std::accumulate(primes.begin() + 1, 
			                           primes.begin() + this->_threshold + 1, 
									   (T)1, 
									   std::multiplies<T>());

		T max_alpha = (r_product - secret_value) / primes[0];

		//T alpha = __uintX2<>::random() % max_alpha;
		T alpha = rand() % max_alpha;

		T S = secret_value + primes[0] * alpha;

		//printf("Shared secret is %u\n", S);
        share_item_CRT<T> share_item;
		share_item.m0 = primes[0];

		for(numbers i = 1; i <= players_count; ++i)
		{
			share_item.modulus = primes[i];
			share_item.remainder = S % primes[i];
			const share_CRT<share_item_CRT<T> > share(share_item);
			//const share_CRT<T> *const p_share = &share;
            
            //  players' numbering begins with 0 but primes' one with 1
			players[i-1]->set_share(&share, i);
		}

     }

     virtual secret_CRT<T> *const restore_secret(
         const ss_players<share_item_CRT<T> >& players) const
     {
         ts_dealer::restore_secret(players);

         share_item_CRT<T> share = players[0]->get_share();
         T m0(share.m0);

         for(ss_players<share_item_CRT<T> >::const_iterator i = players.begin() + 1; i < players.end(); ++i)
         {
            share = (*i)->get_share();
            if(share.m0 != m0) throw "Public parts of shares is not the same";
         }

		 std::vector<T> rems, mods;
		 for(ss_players<share_item_CRT<T> >::const_iterator i = players.begin(); i < players.end(); ++i)
         {
            share = (*i)->get_share();
			rems.push_back(share.remainder);
			mods.push_back(share.modulus);
         }

		 T S = solve_congruences_system_with_CRT(rems, mods);
		 //printf("Restored secret is %u\n", S);
        

         return &secret_CRT<T>(S % m0);
     }
};


/*
template<typename shareT, typename secretT>
class secret_sharing_CRT: public threshold_scheme<shareT, secretT>
{
public:
    secret_sharing_CRT(const size_t threshold, const size_t number, ss_dealer<shareT, secretT> *const dealer):
      threshold_scheme(threshold, number, dealer){}

     virtual ss_secret<secretT> restore_secret(
        const ss_players<shareT> players) const
     {
         if(!is_able_to_restore(players.size())) throw "Not enough players to restore the secret";



        // for(
     }
};
*/